package org.momo.rpc.provider;

import lombok.extern.slf4j.Slf4j;
import org.momo.rpc.annotion.RateLimit;
import org.momo.rpc.annotion.Retry;
import org.momo.rpc.bootstrap.model.ProviderMetaInfo;
import org.momo.rpc.constant.Constant;
import org.momo.rpc.provider.model.ProviderInfo;
import org.momo.rpc.ratelimit.RateLimitProvider;
import org.momo.rpc.ratelimit.RateLimiter;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class ServiceProvider<T> {


    private static final Map<String, ProviderInfo> services = new ConcurrentHashMap<>();

    private static final Map<String, Object> serviceInstances = new ConcurrentHashMap<>();


    public static void provideService(ProviderMetaInfo provider) {
        String serviceName = provider.getServiceName();
        String serviceVersion = provider.getServiceVersion();
        String serviceKey = String.join(Constant.SERVICE_DELIMITER, serviceName, serviceVersion);
        Class<?> serviceClass = provider.getServiceClass();

        // avoid repeat registry
        if (services.containsKey(serviceKey)) {
            log.warn("Service {} {} already exists", serviceName, serviceVersion);
            return;
        }

        // create instance
        Object serviceImpl;
        try {
            serviceImpl = serviceClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("Create instance {} error, error is {}", serviceName, e.getMessage());
            throw new RuntimeException(e);
        }

        // rate limit
        Map<String, RateLimiter> rateLimitMap = new HashMap<>();
        for(Method method : serviceClass.getMethods()) {
            if(method.isAnnotationPresent(RateLimit.class)) {
                RateLimit rateLimit = method.getAnnotation(RateLimit.class);
                RateLimiter rateLimiter = RateLimitProvider.getRateLimiter(rateLimit);
                if(rateLimiter != null) {
                    rateLimitMap.put(method.getName(), rateLimiter);
                }
            }
        }

        ProviderInfo providerInfo = ProviderInfo.builder()
                .serviceName(serviceName)
                .serviceVersion(serviceVersion)
                .serviceKey(serviceKey)
                .serviceClass(serviceClass)
                .rateLimitMap(rateLimitMap)
                .build();
        services.put(serviceKey, providerInfo);
        serviceInstances.put(serviceKey, serviceImpl);
    }

    public static ProviderInfo getServiceClass(String serviceKey) {
        return services.get(serviceKey);
    }

    public static Object getServiceInstance(String serviceKey) {
        return serviceInstances.get(serviceKey);
    }

}
